#include "Acceptor.hpp"
#include "Channel.hpp"
#include "EventLoop.hpp"

#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <cstring>
#include <iostream>
#include <cassert>

static int setNonBlocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) return -1;
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

int Acceptor::createNonblockingSocket() {
    int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
    if (sockfd < 0) {
        perror("socket");
        abort();
    }
    return sockfd;
}

Acceptor::Acceptor(EventLoop* loop, const sockaddr_in& listenAddr)
    : loop_(loop),
      listenFd_(createNonblockingSocket()),
      listenAddr_(listenAddr),
      acceptChannel_(new Channel(loop_, listenFd_)) {

    int optval = 1;
    if (setsockopt(listenFd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
        perror("setsockopt SO_REUSEADDR");
    }

    if (::bind(listenFd_, (sockaddr*)&listenAddr_, sizeof(listenAddr_)) < 0) {
        perror("bind");
        abort();
    }

    // 监听 socket 不立刻 listen，等调用 listen() 方法时再监听
}

Acceptor::~Acceptor() {
    ::close(listenFd_);
}

void Acceptor::listen() {
    if (::listen(listenFd_, SOMAXCONN) < 0) {
        perror("listen");
        abort();
    }

    // 设置读事件回调，监听新连接
    acceptChannel_->setReadCallback(std::bind(&Acceptor::handleRead, this));
    acceptChannel_->enableReading();
}

void Acceptor::handleRead() {
    sockaddr_in peerAddr;
    socklen_t addrlen = sizeof(peerAddr);
    int connfd = ::accept4(listenFd_, (sockaddr*)&peerAddr, &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
    if (connfd < 0) {
        perror("accept4");
        return;
    }
    if (newConnectionCallback_) {
        newConnectionCallback_(connfd, peerAddr);
    } else {
        ::close(connfd);
    }
}
